home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / mkmsgsrc.zip / MKMSGHUD.PAS < prev    next >
Pascal/Delphi Source File  |  1992-09-19  |  47KB  |  1,719 lines

  1. Unit MKMsgHud;        {Hudson/QuickBbs-style Message Base}
  2.  
  3. {$I MKB.Def}
  4.  
  5. {$X+}
  6.  
  7.  
  8. Interface
  9.  
  10.  
  11. Uses MKMsgAbs, MKGlobT;
  12.  
  13. {$I Struct.Hud}
  14.  
  15.  
  16. Const TxtSize = 130;
  17. Const SeekSize = 250;
  18. Const YourSize = 100;
  19.  
  20. Const HudsonFlushing: Boolean = True;
  21. Const HudsonLast: String = '';
  22. Const HudsonEcho: String = '';
  23.  
  24. Type TxtRecsType = Array[1..TxtSize] of MsgTxtType;
  25. Type SeekArrayType = Array[1..SeekSize] of MsgIdxType;
  26.  
  27. Type YourSearchType = Record
  28.   NumRead: Word;
  29.   SeekStart: Integer;
  30.   CurrPos: Integer;
  31.   MsgFound: Boolean;
  32.   Name: String[35];
  33.   Handle: String[35];
  34.   SearchArray: Array[1..YourSize] of String[35];
  35.   End;
  36.  
  37.  
  38. Type HudsonMsgType = Record
  39.   MsgPath: String[50];                 {Message base directory}
  40.   MsgInfoFile: File;
  41.   MsgTxtFile: File;
  42.   MsgHdrFile: File;
  43.   MsgToIdxFile: File;
  44.   MsgIdxFile: File;
  45.   Opened: Boolean;
  46.   Locked: Boolean;
  47.   Error: Word; {0=no error}
  48.   MsgHdr: MsgHdrType;                  {Current message header}
  49.   MsgInfo: MsgInfoType;                {MsgInfo record}
  50.   MsgPos: Word;                        {MsgHdr seek position of current rec}
  51.   SeekNumRead: Word;                   {Number of records in the array}
  52.   SeekPos: Integer;                    {Current position in array}
  53.   SeekStart: Word;                     {File Pos of 1st record in Idx Array}
  54.   SeekOver: Boolean;                   {More idx records?}
  55.   CurrMsgNum: Word;                    {Current Seek Msg number}
  56.   CurrTxtRec: Word;                    {Current txtrec in current msg}
  57.   CurrTxtPos: Word;                    {Current position in current txtrec}
  58.   EOM: Boolean;                        {end of message text}
  59.   LastSoft: Boolean;                   {Last line was soft-wrapped}
  60.   OrigPoint: Word;                     {Point Addr orig}
  61.   DestPoint: Word;                     {Point Addr destination}
  62.   Echo: Boolean;                       {Should message be exported}
  63.   CRLast: Boolean;                     {Last char was CR #13}
  64.   Area: Word;
  65.   MT: MsgMailType;
  66.   End;
  67.  
  68.  
  69.  
  70. Type HudsonMsgObj = Object(AbsMsgObj)  {Message Export Object}
  71.   MsgRec: ^HudsonMsgType;
  72.   MsgChars: ^TxtRecsType;
  73.   SeekArray: ^SeekArrayType;
  74.   YourInfo: ^YourSearchType;
  75.   Constructor Init; {Initialize}
  76.   Destructor Done; Virtual; {Done}
  77.   Procedure MsgStartUp; Virtual; {Setup message/read header}
  78.   Procedure MsgTxtStartUp; Virtual; {Setup message text}
  79.   Function  EOM: Boolean; Virtual; {No more msg text}
  80.   Function  GetChar: Char; Virtual; {Get msg text character}
  81.   Function  NextChar(Var Rec: Word; Var PPos: Word): Boolean;
  82.                                        {internal to position for char}
  83.   Function  GetString(MaxLen: Word): String; Virtual; {Get wordwrapped string}
  84.   Function  WasWrap: Boolean; Virtual; {Last line was soft wrapped no CR}
  85.   Procedure SeekFirst(MsgNum: LongInt); Virtual; {Seek msg number}
  86.   Procedure SeekNext; Virtual; {Find next matching msg}
  87.   Procedure SeekPrior; Virtual; {Find prior matching msg}
  88.   Procedure SeekRead(NumToRead: Word); {Refill seek array}
  89.   Function  GetFrom: String; Virtual; {Get from name on current msg}
  90.   Function  GetTo: String;Virtual; {Get to name on current msg}
  91.   Function  GetSubj: String; Virtual; {Get subject on current msg}
  92.   Function  GetCost: Word; Virtual; {Get cost of current msg}
  93.   Function  GetDate: String; Virtual; {Get date of current msg}
  94.   Function  GetTime: String; Virtual; {Get time of current msg}
  95.   Function  GetRefer: LongInt; Virtual; {Get reply to of current msg}
  96.   Function  GetSeeAlso: LongInt; Virtual; {Get see also of current msg}
  97.   Function  GetMsgNum: LongInt; Virtual; {Get message number}
  98.   Procedure GetOrig(Var Addr: AddrType); Virtual; {Get origin address}
  99.   Procedure GetDest(Var Addr: AddrType); Virtual; {Get destination address}
  100.   Function  IsLocal: Boolean; Virtual; {Is current msg local}
  101.   Function  IsCrash: Boolean; Virtual; {Is current msg crash}
  102.   Function  IsKillSent: Boolean; Virtual; {Is current msg kill sent}
  103.   Function  IsSent: Boolean; Virtual; {Is current msg sent}
  104.   Function  IsFAttach: Boolean; Virtual; {Is current msg file attach}
  105.   Function  IsReqRct: Boolean; Virtual; {Is current msg request receipt}
  106.   Function  IsReqAud: Boolean; Virtual; {Is current msg request audit}
  107.   Function  IsRetRct: Boolean; Virtual; {Is current msg a return receipt}
  108.   Function  IsFileReq: Boolean; Virtual; {Is current msg a file request}
  109.   Function  IsRcvd: Boolean; Virtual; {Is current msg received}
  110.   Function  IsPriv: Boolean; Virtual; {Is current msg priviledged/private}
  111.   Function  IsDeleted: Boolean; Virtual; {Is current msg deleted}
  112.   Function  IsEchoed: Boolean; Virtual; {Is current msg unmoved echomail msg}
  113.   Procedure YoursFirst(Name: String; Handle: String); Virtual; {Search for mail to caller}
  114.   Procedure YoursNext; Virtual; {Search for next message}
  115.   Function  YoursFound: Boolean; Virtual; {Found a message}
  116.   Procedure SetDest(Var Addr: AddrType); Virtual; {Set Zone/Net/Node/Point for Dest}
  117.   Procedure SetOrig(Var Addr: AddrType); Virtual; {Set Zone/Net/Node/Point for Orig}
  118.   Procedure SetFrom(Name: String); Virtual; {Set message from}
  119.   Procedure SetTo(Name: String); Virtual; {Set message to}
  120.   Procedure SetSubj(Str: String); Virtual; {Set message subject}
  121.   Procedure SetCost(SCost: Word); Virtual; {Set message cost}
  122.   Procedure SetRefer(SRefer: LongInt); Virtual; {Set message reference}
  123.   Procedure SetSeeAlso(SAlso: LongInt); Virtual; {Set message see also}
  124.   Procedure SetDate(SDate: String); Virtual; {Set message date}
  125.   Procedure SetTime(STime: String); Virtual; {Set message time}
  126.   Procedure SetEcho(ES: Boolean); Virtual; {Set echo status}
  127.   Procedure SetMsgAttr(Setting: Boolean; Mask: Word);
  128.   Procedure SetNetAttr(Setting: Boolean; Mask: Word);
  129.   Procedure SetLocal(LS: Boolean); Virtual; {Set local status}
  130.   Procedure SetRcvd(RS: Boolean); Virtual; {Set received status}
  131.   Procedure SetPriv(PS: Boolean); Virtual; {Set priveledge vs public status}
  132.   Procedure SetCrash(SS: Boolean); Virtual; {Set crash netmail status}
  133.   Procedure SetKillSent(SS: Boolean); Virtual; {Set kill/sent netmail status}
  134.   Procedure SetSent(SS: Boolean); Virtual; {Set sent netmail status}
  135.   Procedure SetFAttach(SS: Boolean); Virtual; {Set file attach status}
  136.   Procedure SetReqRct(SS: Boolean); Virtual; {Set request receipt status}
  137.   Procedure SetReqAud(SS: Boolean); Virtual; {Set request audit status}
  138.   Procedure SetRetRct(SS: Boolean); Virtual; {Set return receipt status}
  139.   Procedure SetFileReq(SS: Boolean); Virtual; {Set file request status}
  140.   Procedure DoString(Str: String); Virtual; {Add string to message text}
  141.   Procedure DoChar(Ch: Char); Virtual; {Add character to message text}
  142.   Procedure DoStringLn(Str: String); Virtual; {Add string and newline to msg text}
  143.   Function  WriteMsg: Word; Virtual; {Write msg to message base}
  144.   Function  OpenMsgBase: Word; Virtual; {Individual msg open}
  145.   Function  CloseMsgBase: Word; Virtual; {Individual msg close}
  146.   Function  SeekEnd: Word; Virtual; {Seek to eof for msg base files}
  147.   Function  SeekMsgBasePos(Position: Word): Word; Virtual; {Seek to pos of Msg Base File}
  148.   Function  Check: Word; Virtual; {Check if msg base is ok}
  149.   Function  CreateMsgBase(MaxMsg: Word; MaxDays: Word): Word; Virtual;{Create initial msg base files}
  150.   Function  LockMsgBase: Boolean; Virtual; {Lock msg base for updating}
  151.   Function  UnlockMsgBase: Boolean; Virtual; {Unlock msg base after updating}
  152.   Function  WriteMailIdx(FN: String; MsgPos: Word): Word; Virtual;
  153.     {Write Netmail or EchoMail.Bbs}
  154.   Function  MsgBaseSize: Word; Virtual; {Number of msg base index records}
  155.   Function  GetNumActive: Word; Virtual; {Get number of active messages}
  156.   Function  GetHighMsgNum: LongInt; Virtual; {Get highest msg number}
  157.   Function  GetLowMsgNum: LongInt; Virtual; {Get lowest msg number}
  158.   Procedure StartNewMsg; Virtual; {Initialize message}
  159.   Procedure SetMsgPath(MP: String); Virtual;
  160.   Function  SeekFound:Boolean; Virtual; {Seek msg found}
  161.   Procedure SetMailType(MT: MsgMailType); Virtual; {Set message base type}
  162.   Function  GetSubArea: Word; Virtual; {Get sub area number}
  163.   Procedure ReWriteHdr; Virtual; {Rewrite msg header after changes}
  164.   Procedure DeleteMsg; Virtual; {Delete current message}
  165.   Function  GetMsgLoc: LongInt; Virtual; {To allow reseeking to message}
  166.   Procedure SetMsgLoc(ML: LongInt); Virtual; {Reseek to message}
  167.   Function  NumberOfMsgs: LongInt; Virtual; {Number of messages}
  168.   Function  GetLastRead(UNum: LongInt): LongInt; Virtual; {Get last read for user num}
  169.   Procedure SetLastRead(UNum: LongInt; LR: LongInt); Virtual; {Set last read}
  170.   Procedure GetAllLastRead(UNum: LongInt; Var LR: LastReadType); Virtual; {all areas}
  171.   Procedure GetHighest(Var LR: LastReadType); Virtual; {Get highest all areas}
  172.   Function  GetTxtPos: LongInt; Virtual;
  173.   Procedure SetTxtPos(TP: LongInt); Virtual;
  174.   End;
  175.  
  176. Type HudsonMsgPtr = ^HudsonMsgObj;
  177.  
  178. Implementation
  179.  
  180. Uses
  181.   MKFile, MKString;
  182.  
  183.  
  184. Constructor HudsonMsgObj.Init;
  185.   Begin
  186.   New(MsgRec);
  187.   New(MsgChars);
  188.   New(SeekArray);
  189.   New(YourInfo);
  190.   If ((MsgRec = Nil) Or (MsgChars = Nil) or (SeekArray = Nil) or (YourInfo = Nil)) Then
  191.     Begin
  192.     If MsgRec <> Nil Then
  193.       Dispose(MsgRec);
  194.     If MsgChars <> Nil Then
  195.       Dispose(MsgChars);
  196.     If SeekArray <> Nil Then
  197.       Dispose(SeekArray);
  198.     If YourInfo <> Nil Then
  199.       Dispose(YourInfo);
  200.     Fail;
  201.     Exit;
  202.     End;
  203.   MsgRec^.MsgPath := '';
  204.   MsgRec^.Opened := False;
  205.   MsgRec^.Locked := False;
  206.   MsgRec^.Error := 0;
  207.   End;
  208.  
  209.  
  210. Procedure HudsonMsgObj.YoursFirst(Name: String; Handle: String);
  211.   Begin
  212.   YourInfo^.NumRead := 0;
  213.   YourInfo^.SeekStart := 0;
  214.   YourInfo^.CurrPos := 1;
  215.   YourInfo^.MsgFound := False;
  216.   YourInfo^.Name := Copy(StripBoth(Upper(Name), ' '),1, 35);
  217.   YourInfo^.Handle := Copy(StripBoth(Upper(Handle), ' '),1,35);
  218.   YoursNext;
  219.   End;
  220.  
  221.  
  222. Procedure HudsonMsgObj.YoursNext;
  223.   Var
  224.     SearchOver: Boolean;
  225.  
  226.   Begin
  227.   Inc(YourInfo^.CurrPos);
  228.   SearchOver := False;
  229.   YourInfo^.MsgFound := False;
  230.   While Not SearchOver Do
  231.     Begin
  232.     If YourInfo^.CurrPos > YourInfo^.NumRead Then
  233.       Begin
  234.       Inc(YourInfo^.SeekStart, YourInfo^.NumRead);
  235.       YourInfo^.CurrPos := 1;
  236.       Seek(MsgRec^.MsgToIdxFile, YourInfo^.SeekStart);
  237.       If IoResult <> 0 Then
  238.         YourInfo^.NumRead := 0;
  239.       If Not shRead(MsgRec^.MsgToIdxFile,
  240.       YourInfo^.SearchArray, SizeOf(YourInfo^.SearchArray), YourInfo^.NumRead) Then
  241.         Begin
  242.         MsgRec^.Error := 1000;
  243.         YourInfo^.NumRead := 0;
  244.         End;
  245.       End;
  246.     If YourInfo^.NumRead = 0 Then
  247.       SearchOver := True
  248.     Else
  249.       Begin
  250.       If (((Upper(YourInfo^.SearchArray[YourInfo^.CurrPos]) = YourInfo^.Name) Or
  251.       (Upper(YourInfo^.SearchArray[YourInfo^.CurrPos]) = YourInfo^.Handle)) And
  252.       ((YourInfo^.CurrPos > 0) And (YourInfo^.CurrPos <= YourInfo^.NumRead)))Then
  253.         Begin
  254.         MsgRec^.MsgPos := YourInfo^.SeekStart + YourInfo^.CurrPos - 1;
  255.         MsgStartUp;
  256.         If Not (IsRcvd)  Then
  257.           Begin
  258.           YourInfo^.MsgFound := True;
  259.           SearchOver := True;
  260.           End;
  261.         End;
  262.       End;
  263.     If Not YourInfo^.MsgFound Then
  264.       Inc(YourInfo^.CurrPos);
  265.     End;
  266.   End;
  267.  
  268.  
  269. Function  HudsonMsgObj.YoursFound: Boolean;
  270.   Begin
  271.   YoursFound := YourInfo^.MsgFound;
  272.   End;
  273.  
  274.  
  275. Function HudsonMsgObj.WasWrap: Boolean;
  276.   Begin
  277.   WasWrap := MsgRec^.LastSoft;
  278.   End;
  279.  
  280.  
  281. Destructor HudsonMsgObj.Done;
  282.   Begin
  283.   Dispose(MsgRec);
  284.   Dispose(MsgChars);
  285.   Dispose(SeekArray);
  286.   Dispose(YourInfo);
  287.   End;
  288.  
  289.  
  290. Procedure HudsonMsgObj.MsgStartUp;
  291.   Var
  292.     NumRead: Word;
  293.  
  294.   Begin
  295.   MsgRec^.Error := SeekMsgBasePos(MsgRec^.MsgPos);
  296.   MsgRec^.OrigPoint := 0;
  297.   MsgRec^.DestPoint := 0;
  298.   If MsgRec^.Error = 0 Then
  299.     Begin
  300.     If not shRead(MsgRec^.MsgHdrFile, MsgRec^.MsgHdr,1, NumRead) Then
  301.       MsgRec^.Error := FileError;
  302.     End;
  303.   End;
  304.  
  305.  
  306. Procedure HudsonMsgObj.SetMsgAttr(Setting: Boolean; Mask: Word);
  307.   Begin
  308.   If Setting Then
  309.     MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr or Mask
  310.   Else
  311.     MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr and (Not Mask);
  312.   End;
  313.  
  314.  
  315.  
  316. Procedure HudsonMsgObj.SetRcvd(RS: Boolean);
  317.   Begin
  318.   SetMsgAttr(RS, maRcvd);
  319.   End;
  320.  
  321.  
  322. Procedure HudsonMsgObj.SetPriv(PS: Boolean);
  323.   Begin
  324.   SetMsgAttr(PS, maPriv);
  325.   End;
  326.  
  327.  
  328. Procedure HudsonMsgObj.SetNetAttr(Setting: Boolean; Mask: Word);
  329.   Begin
  330.   If Setting Then
  331.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr Or Mask
  332.   Else
  333.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr And (Not Mask);
  334.   End;
  335.  
  336.  
  337. Procedure HudsonMsgObj.SetSeeAlso(SAlso: LongInt);
  338.   Begin
  339.   MsgRec^.MsgHdr.SeeAlso := SAlso;
  340.   End;
  341.  
  342.  
  343. Procedure HudsonMsgObj.SetFrom(Name: String); {Set msg from}
  344.   Begin
  345.   MsgRec^.MsgHdr.MsgFrom := Name;
  346.   End;
  347.  
  348.  
  349. Procedure HudsonMsgObj.SetTo(Name: String); {Set msg to}
  350.   Begin
  351.   MsgRec^.MsgHdr.MsgTo := Name;
  352.   End;
  353.  
  354.  
  355. Procedure HudsonMsgObj.SetSubj(Str: String); {Set msg subject}
  356.   Begin
  357.   MsgRec^.MsgHdr.Subj := Str;
  358.   End;
  359.  
  360.  
  361. Function HudsonMsgObj.GetFrom: String;
  362.   Begin
  363.   GetFrom := MsgRec^.MsgHdr.MsgFrom;
  364.   End;
  365.  
  366.  
  367. Function HudsonMsgObj.GetTo: String;
  368.   Begin
  369.   GetTo := MsgRec^.MsgHdr.MsgTo;
  370.   End;
  371.  
  372.  
  373. Function HudsonMsgObj.GetSubj: String;
  374.   Begin
  375.   GetSubj := MsgRec^.MsgHdr.Subj;
  376.   End;
  377.  
  378.  
  379. Function HudsonMsgObj.GetCost: Word;
  380.   Begin
  381.   GetCost := MsgRec^.MsgHdr.Cost;
  382.   End;
  383.  
  384.  
  385. Function HudsonMsgObj.GetDate: String;            {Get date of current msg}
  386.   Begin
  387.   GetDate := MsgRec^.MsgHdr.Date;
  388.   End;
  389.  
  390.  
  391. Function HudsonMsgObj.GetTime: String;            {Get time of current msg}
  392.   Begin
  393.   GetTime := MsgRec^.MsgHdr.Time;
  394.   End;
  395.  
  396.  
  397. Function HudsonMsgObj.GetRefer: LongInt;
  398.   Begin
  399.   GetRefer := MsgRec^.MsgHdr.ReplyTo;
  400.   End;
  401.  
  402.  
  403. Function HudsonMsgObj.GetSeeAlso: LongInt;
  404.   Begin
  405.   GetSeeAlso := MsgRec^.MsgHdr.SeeAlso;
  406.   End;
  407.  
  408.  
  409. Function HudsonMsgObj.GetMsgNum: LongInt;
  410.   Begin
  411.   GetMsgNum := MsgRec^.MsgHdr.MsgNum;
  412.   End;
  413.  
  414.  
  415. Procedure HudsonMsgObj.GetOrig(Var Addr: AddrType);
  416.   Begin
  417.   Addr.Zone := MsgRec^.MsgHdr.OrigZone;
  418.   Addr.Net := MsgRec^.MsgHdr.OrigNet;
  419.   Addr.Node := MsgRec^.MsgHdr.OrigNode;
  420.   Addr.Point := MsgRec^.OrigPoint;
  421.   End;
  422.  
  423.  
  424. Procedure HudsonMsgObj.GetDest(Var Addr: AddrType);
  425.   Begin
  426.   Addr.Zone := MsgRec^.MsgHdr.DestZone;
  427.   Addr.Net := MsgRec^.MsgHdr.DestNet;
  428.   Addr.Node := MsgRec^.MsgHdr.DestNode;
  429.   Addr.Point := MsgRec^.DestPoint;
  430.   End;
  431.  
  432.  
  433. Function HudsonMsgObj.IsLocal: Boolean;
  434.   Begin
  435.   IsLocal := ((MsgRec^.MsgHdr.MsgAttr and maLocal) <> 0);
  436.   End;
  437.  
  438.  
  439. Function HudsonMsgObj.IsCrash: Boolean;
  440.   Begin
  441.   IsCrash := ((MsgRec^.MsgHdr.NetAttr and naCrash) <> 0);
  442.   End;
  443.  
  444.  
  445. Function HudsonMsgObj.IsKillSent: Boolean;
  446.   Begin
  447.   IsKillSent := ((MsgRec^.MsgHdr.NetAttr and naKillSent) <> 0);
  448.   End;
  449.  
  450.  
  451. Function HudsonMsgObj.IsSent: Boolean;
  452.   Begin
  453.   IsSent := ((MsgRec^.MsgHdr.NetAttr and naSent) <> 0);
  454.   End;
  455.  
  456.  
  457. Function HudsonMsgObj.IsFAttach: Boolean;
  458.   Begin
  459.   IsFAttach := ((MsgRec^.MsgHdr.NetAttr and naFAttach) <> 0);
  460.   End;
  461.  
  462.  
  463. Function HudsonMsgObj.IsReqRct: Boolean;
  464.   Begin
  465.   IsReqRct := ((MsgRec^.MsgHdr.NetAttr and naReqRcpt) <> 0);
  466.   End;
  467.  
  468.  
  469. Function HudsonMsgObj.IsReqAud: Boolean;
  470.   Begin
  471.   IsReqAud := ((MsgRec^.MsgHdr.NetAttr and naReqAudit) <> 0);
  472.   End;
  473.  
  474.  
  475. Function HudsonMsgObj.IsRetRct: Boolean;
  476.   Begin
  477.   IsRetRct := ((MsgRec^.MsgHdr.NetAttr and naRetRcpt) <> 0);
  478.   End;
  479.  
  480.  
  481. Function HudsonMsgObj.IsFileReq: Boolean;
  482.   Begin
  483.   IsFileReq := ((MsgRec^.MsgHdr.NetAttr and naFileReq) <> 0);
  484.   End;
  485.  
  486.  
  487. Function HudsonMsgObj.IsRcvd: Boolean;
  488.   Begin
  489.   IsRcvd := ((MsgRec^.MsgHdr.MsgAttr and maRcvd) <> 0);
  490.   End;
  491.  
  492.  
  493. Function HudsonMsgObj.IsPriv: Boolean;
  494.   Begin
  495.   IsPriv := ((MsgRec^.MsgHdr.MsgAttr and maPriv) <> 0);
  496.   End;
  497.  
  498.  
  499. Function HudsonMsgObj.IsDeleted: Boolean;
  500.   Begin
  501.   IsDeleted := ((MsgRec^.MsgHdr.MsgAttr and maDeleted) <> 0);
  502.   End;
  503.  
  504.  
  505. Function HudsonMsgObj.IsEchoed: Boolean;
  506.   Begin
  507.   IsEchoed := MsgRec^.Echo;
  508. {  IsEchoed := ((MsgRec^.MsgHdr.MsgAttr and maUnmovedEcho) <> 0); }
  509. {  IsUnmovedNet := ((MsgRec^.MsgHdr.MsgAttr and maUnmovedNet) <> 0);}
  510.   End;
  511.  
  512.  
  513. Procedure HudsonMsgObj.MsgTxtStartUp;
  514.   Var
  515.     NumRead: Word;
  516.     MaxTxt: Word;
  517.  
  518.   Begin
  519.   MsgRec^.LastSoft := False;
  520.   If MsgRec^.MsgHdr.NumRecs > TxtSize Then
  521.     MaxTxt := TxtSize
  522.   Else
  523.     MaxTxt := MsgRec^.MsgHdr.NumRecs;
  524.   Seek(MsgRec^.MsgTxtFile, MsgRec^.MsgHdr.StartRec);
  525.   If IoResult <> 0 Then
  526.     MsgRec^.Error := 2222;
  527.   If not shRead(MsgRec^.MsgTxtFile, MsgChars^, MaxTxt, NumRead) Then
  528.     MsgRec^.Error := FileError;
  529.   If NumRead <> MaxTxt Then
  530.     MsgRec^.Error := 1111;
  531.   MsgRec^.CurrTxtRec := 1;
  532.   MsgRec^.CurrTxtPos := 1;
  533.   MsgRec^.EOM := False;
  534.   End;
  535.  
  536.  
  537. Function HudsonMsgObj.NextChar(Var Rec: Word; Var PPos: Word): Boolean;
  538.   Var
  539.     MoreNext: Boolean;
  540.  
  541.   Begin
  542.   MoreNext := True;
  543.   NextChar := True;
  544.   While MoreNext Do
  545.     Begin
  546.     If ((Rec > MsgRec^.MsgHdr.NumRecs) or (Rec > TxtSize)) Then
  547.       MoreNext := False
  548.     Else
  549.       Begin
  550.       If (PPos > Length(MsgChars^[Rec])) Then
  551.         Begin
  552.         Inc(Rec);
  553.         PPos := 1;
  554.         End
  555.       Else
  556.         MoreNext := False;
  557.       End;
  558.     End;
  559.   If ((Rec > MsgRec^.MsgHdr.NumRecs) or (Rec > TxtSize)) Then
  560.     NextChar := False;
  561.   End;
  562.  
  563.  
  564. Function HudsonMsgObj.GetString(MaxLen: Word): String;
  565.   Var
  566.     Rec: Word;
  567.     PPos: Word;
  568.     CurrLen: Byte;
  569.     WRec: Word;
  570.     WPos: Word;
  571.     WLen: Byte;
  572.     StrDone: Boolean;
  573.     TxtOver: Boolean;
  574.     StartSoft: Boolean;
  575.  
  576.   Begin
  577.   StrDone := False;
  578.   CurrLen := 0;
  579.   Rec := MsgRec^.CurrTxtRec;
  580.   PPos := MsgRec^.CurrTxtPos;
  581.   TxtOver := Not NextChar(Rec, PPos);
  582.   If TxtOver Then
  583.     MsgRec^.EOM := True;
  584.   WLen := 0;
  585.   WRec := Rec;
  586.   WPos := PPos;
  587.   StartSoft := MsgRec^.LastSoft;
  588.   MsgRec^.LastSoft := True;
  589.   While ((Not StrDone) And (CurrLen < MaxLen) And (Not TxtOver)) Do
  590.     Begin
  591.     Case MsgChars^[Rec][PPos] of
  592.       #$0d: Begin
  593.             StrDone := True;
  594.             MsgRec^.LastSoft := False;
  595.             End;
  596.       #$8d:;
  597.       #$0a:;
  598.       #$20: Begin
  599.             If ((CurrLen <> 0) or (Not StartSoft)) Then
  600.               Begin
  601.               Inc(CurrLen);
  602.               GetString[CurrLen] := MsgChars^[Rec][PPos];
  603.               WLen := CurrLen;
  604.               WRec := Rec;
  605.               WPos := PPos;
  606.               End
  607.             Else
  608.               StartSoft := False;
  609.             End;
  610.       Else
  611.         Begin
  612.         Inc(CurrLen);
  613.         GetString[CurrLen] := MsgChars^[Rec][PPos];
  614.         End;
  615.       End;
  616.     Inc(PPos);
  617.     TxtOver := Not NextChar(Rec, PPos);
  618.     End;
  619.   If StrDone Then
  620.     Begin
  621.     GetString[0] := Chr(CurrLen);
  622.     MsgRec^.CurrTxtRec := Rec;
  623.     MsgRec^.CurrTxtPos := PPos;
  624.     End
  625.   Else
  626.     If TxtOver Then
  627.       Begin
  628.       GetString[0] := Chr(CurrLen);
  629.       MsgRec^.CurrTxtRec := Rec;
  630.       MsgRec^.CurrTxtPos := PPos;
  631.       If CurrLen = 0 Then
  632.         MsgRec^.EOM := True;
  633.       End
  634.     Else
  635.       Begin
  636.       If WLen = 0 Then
  637.         Begin
  638.         GetString[0] := Chr(CurrLen);
  639.         MsgRec^.CurrTxtRec := Rec;
  640.         MsgRec^.CurrTxtPos := PPos;
  641.         End
  642.       Else
  643.         Begin
  644.         GetString[0] := Chr(WLen);
  645.         Inc(WPos);
  646.         NextChar(WRec, WPos);
  647.         MsgRec^.CurrTxtPos := WPos;
  648.         MsgRec^.CurrTxtRec := WRec;
  649.         End;
  650.       End;
  651.   End;
  652.  
  653.  
  654. Function HudsonMsgObj.GetChar: Char;
  655.   Var
  656.     MoreNext: Boolean;
  657.  
  658.   Begin
  659.   MoreNext := True;
  660.   If ((MsgRec^.CurrTxtRec <= MsgRec^.MsgHdr.NumRecs) and
  661.   (MsgRec^.CurrTxtRec <= TxtSize)) Then
  662.     Begin
  663.     While MoreNext Do
  664.       Begin
  665.       If ((MsgRec^.CurrTxtRec > MsgRec^.MsgHdr.NumRecs) Or
  666.       (MsgRec^.CurrTxtRec > TxtSize)) Then
  667.         MoreNext := False
  668.       Else
  669.         Begin
  670.         If (MsgRec^.CurrTxtPos > Length(MsgChars^[MsgRec^.CurrTxtRec])) Then
  671.           Begin
  672.           Inc(MsgRec^.CurrTxtRec);
  673.           MsgRec^.CurrTxtPos := 1;
  674.           End
  675.         Else
  676.           MoreNext := False;
  677.         End;
  678.       End;
  679.     If ((MsgRec^.CurrTxtRec > MsgRec^.MsgHdr.NumRecs) Or
  680.     (MsgRec^.CurrTxtRec > TxtSize)) Then
  681.       MsgRec^.EOM := True;
  682.     End
  683.   Else
  684.     MsgRec^.EOM := True;
  685.   If MsgRec^.EOM Then
  686.     Begin
  687.     GetChar := #0;
  688.     End
  689.   Else
  690.     GetChar := MsgChars^[MsgRec^.CurrTxtRec][MsgRec^.CurrTxtPos];
  691.   Inc(MsgRec^.CurrTxtPos);
  692.   End;
  693.  
  694.  
  695. Function HudsonMsgObj.EOM: Boolean;
  696.   Begin
  697.   EOM := MsgRec^.EOM;
  698.   End;
  699.  
  700.  
  701. Procedure HudsonMsgObj.StartNewMsg;  {Initialize message}
  702.   Const
  703.     Blank = '* Blank *';
  704.  
  705.   Begin
  706.   MsgRec^.CurrTxtRec := 1;
  707.   MsgRec^.CurrTxtPos := 0;
  708.   FillChar(MsgRec^.MsgHdr, SizeOf(MsgRec^.MsgHdr), #0);
  709.   MsgRec^.Echo := False;
  710.   MsgRec^.MsgHdr.Time := '00:00';
  711.   MsgRec^.MsgHdr.Date := '00-00-00';
  712.   MsgRec^.MsgHdr.MsgTo := Blank;
  713.   MsgRec^.MsgHdr.MsgFrom := Blank;
  714.   MsgRec^.MsgHdr.Subj := Blank;
  715.   MsgRec^.CRLast := True;
  716.   End;
  717.  
  718.  
  719. Procedure HudsonMsgObj.SetEcho(ES: Boolean); {Set echo status}
  720.   Begin
  721.   MsgRec^.Echo := ES;
  722.   End;
  723.  
  724.  
  725. Procedure HudsonMsgObj.SetLocal(LS: Boolean); {Set local status}
  726.   Begin
  727.   If LS Then
  728.     MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr or maLocal
  729.   Else
  730.     MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.Msgattr and (Not maLocal);
  731.   End;
  732.  
  733.  
  734. Procedure HudsonMsgObj.SetCrash(SS: Boolean); {Set crash netmail status}
  735.   Begin
  736.   If SS Then
  737.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr or naCrash
  738.   Else
  739.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr and (Not naCrash);
  740.   End;
  741.  
  742.  
  743. Procedure HudsonMsgObj.SetKillSent(SS: Boolean); {Set kill/sent netmail status}
  744.   Begin
  745.   If SS Then
  746.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr or naKillSent
  747.   Else
  748.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr and (Not naKillSent);
  749.   End;
  750.  
  751.  
  752.  
  753. Procedure HudsonMsgObj.SetSent(SS: Boolean); {Set sent netmail status}
  754.   Begin
  755.   If SS Then
  756.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr or naSent
  757.   Else
  758.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr and (Not naSent);
  759.   End;
  760.  
  761.  
  762.  
  763. Procedure HudsonMsgObj.SetFAttach(SS: Boolean); {Set file attach status}
  764.   Begin
  765.   If SS Then
  766.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr or naFAttach
  767.   Else
  768.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr and (Not naFAttach);
  769.   End;
  770.  
  771.  
  772.  
  773. Procedure HudsonMsgObj.SetReqRct(SS: Boolean); {Set request receipt status}
  774.   Begin
  775.   If SS Then
  776.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr or naReqRcpt
  777.   Else
  778.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr and (Not naReqRcpt);
  779.   End;
  780.  
  781.  
  782.  
  783. Procedure HudsonMsgObj.SetReqAud(SS: Boolean); {Set request audit status}
  784.   Begin
  785.   If SS Then
  786.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr or naReqAudit
  787.   Else
  788.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr and (Not naReqAudit);
  789.   End;
  790.  
  791.  
  792.  
  793. Procedure HudsonMsgObj.SetRetRct(SS: Boolean); {Set return receipt status}
  794.   Begin
  795.   If SS Then
  796.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr or naRetRcpt
  797.   Else
  798.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr and (Not naRetRcpt);
  799.   End;
  800.  
  801.  
  802.  
  803. Procedure HudsonMsgObj.SetFileReq(SS: Boolean); {Set file request status}
  804.   Begin
  805.   If SS Then
  806.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr or naFileReq
  807.   Else
  808.     MsgRec^.MsgHdr.NetAttr := MsgRec^.MsgHdr.NetAttr and (Not naFileReq);
  809.   End;
  810.  
  811.  
  812. Procedure HudsonMsgObj.SetCost(SCost: Word);      {Set message cost}
  813.   Begin
  814.   MsgRec^.MsgHdr.Cost := SCost;
  815.   End;
  816.  
  817.  
  818. Procedure HudsonMsgObj.SetRefer(SRefer: LongInt);    {Set message reference}
  819.   Begin
  820.   MsgRec^.MsgHdr.ReplyTo := SRefer;
  821.   End;
  822.  
  823.  
  824. Procedure HudsonMsgObj.SetDate(SDate: String);    {Set message date}
  825.   Begin
  826.   MsgRec^.MsgHdr.Date := Copy(PadLeft(SDate,'0',8),1,8);
  827.   MsgRec^.MsgHdr.Date[3] := '-';
  828.   MsgRec^.MsgHdr.Date[6] := '-';
  829.   End;
  830.  
  831.  
  832. Procedure HudsonMsgObj.SetTime(STime: String);    {Set message time}
  833.   Begin
  834.   MsgRec^.MsgHdr.Time := Copy(PadLeft(STime,'0',5),1,5);
  835.   MsgRec^.MsgHdr.Time[3] := ':';
  836.   End;
  837.  
  838.  
  839.  
  840. Procedure HudsonMsgObj.DoString(Str: String);     {Add string to message text}
  841.   Var
  842.     i: Word;
  843.  
  844.   Begin
  845.   i := 1;
  846.   While i <= Length(Str) Do
  847.     Begin
  848.     DoChar(Str[i]);
  849.     Inc(i);
  850.     End;
  851.   End;
  852.  
  853.  
  854. Procedure HudsonMsgObj.DoChar(Ch: Char);          {Add character to message text}
  855.   Begin
  856.   If MsgRec^.CurrTxtPos = 255 Then
  857.     Begin
  858.     MsgChars^[MsgRec^.CurrTxtRec][0] := Chr(255);
  859.     Inc(MsgRec^.CurrTxtRec);
  860.     MsgRec^.CurrTxtPos := 0;
  861.     End;
  862.   Case CH of
  863.     #$0D: MsgRec^.CRLast := True;
  864.     #$0A:;
  865.     #$8D:;
  866.     Else
  867.       MsgRec^.CRLast := False;
  868.     End;
  869.   Inc(MsgRec^.CurrTxtPos);
  870.   MsgChars^[MsgRec^.CurrTxtRec][MsgRec^.CurrTxtPos] := Ch;
  871.   End;
  872.  
  873.  
  874.  
  875. Procedure HudsonMsgObj.DoStringLn(Str: String);   {Add string and newline to msg text}
  876.   Begin
  877.   DoString(Str);
  878.   DoChar(#13);
  879.   End;
  880.  
  881.  
  882. Function HudsonMsgObj.WriteMsg: Word;
  883.   Var
  884.     WriteError: Word;
  885.     MsgPos: Word;
  886.     MsgIdx: MsgIdxType;
  887.     FN: String[13];
  888.     AlreadyLocked: Boolean;
  889.  
  890.   Begin
  891.   If FileSize(MsgRec^.MsgTxtFile) > $ff00 Then
  892.     WriteError := 99
  893.   Else
  894.     WriteError := 0;
  895.   If Not MsgRec^.CRLast Then
  896.     DoChar(#$0D);
  897.   MsgRec^.MsgHdr.NumRecs := MsgRec^.CurrTxtRec;
  898.   MsgChars^[MsgRec^.CurrTxtRec][0] := Chr(MsgRec^.CurrTxtPos);
  899.   Case MsgRec^.MT of
  900.     mmtNormal:  Begin
  901.              MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr and
  902.                Not(maNetMail + maUnmovedNet + maUnmovedEcho);
  903.              End;
  904.     mmtEchoMail: Begin
  905.              MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr and
  906.                Not(maNetMail + maUnmovedNet);
  907.              If MsgRec^.Echo Then
  908.                MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr or maUnmovedEcho;
  909.              End;
  910.     mmtNetMail: Begin
  911.              MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr and Not(maUnmovedEcho);
  912.              MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr or maNetMail;
  913.              If MsgRec^.Echo Then
  914.                MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr or maUnmovedNet;
  915.              End;
  916.     End;
  917.   MsgRec^.MsgHdr.Area := MsgRec^.Area;
  918.   AlreadyLocked := MsgRec^.Locked;
  919.   If Not AlreadyLocked Then
  920.     If Not LockMsgBase Then
  921.       WriteError := 5;
  922.   If WriteError = 0 Then
  923.     WriteError := SeekEnd;
  924.   If WriteError = 0 Then               {Write MsgHdr}
  925.     Begin
  926.     MsgRec^.MsgHdr.StartRec := FileSize(MsgRec^.MsgTxtFile);
  927.     MsgPos := FileSize(MsgRec^.MsgHdrFile);
  928.     Inc(MsgRec^.MsgInfo.HighMsg);
  929.     MsgRec^.MsgHdr.MsgNum := MsgRec^.MsgInfo.HighMsg;
  930.     Inc(MsgRec^.MsgInfo.Active);
  931.     Inc(MsgRec^.MsgInfo.AreaActive[MsgRec^.MsgHdr.Area]);
  932.     If Not shWrite(MsgRec^.MsgHdrFile, MsgRec^.MsgHdr, 1)
  933.       Then WriteError := FileError;
  934.     End;
  935.   If WriteError = 0 Then
  936.     If Not shWrite(MsgRec^.MsgToIdxFile, MsgRec^.MsgHdr.MsgTo, 1) Then
  937.       WriteError := FileError;
  938.   If WriteError = 0 Then               {Write MsgIdx}
  939.     Begin
  940.     MsgIdx.MsgNum := MsgRec^.MsgHdr.MsgNum;
  941.     MsgIdx.Area := MsgRec^.MsgHdr.Area;
  942.     If Not shWrite(MsgRec^.MsgIdxFile, MsgIdx, 1) Then
  943.       WriteError := FileError;
  944.     End;
  945.   If WriteError = 0 Then               {Write MsgTxt}
  946.     Begin
  947.     If Not shWrite(MsgRec^.MsgTxtFile, MsgChars^, MsgRec^.MsgHdr.NumRecs) Then
  948.       WriteError := FileError;
  949.     End;
  950.   If WriteError = 0 Then
  951.     Begin
  952.     Case MsgRec^.MT of
  953.       mmtEchoMail: FN := 'ECHOMAIL.BBS';
  954.       mmtNetMail: FN := 'NETMAIL.BBS';
  955.       Else
  956.         FN := '';
  957.       End; {Case MsgType}
  958.     If ((Length(FN) > 0) and MsgRec^.Echo) Then
  959.       WriteError := WriteMailIdx(FN, MsgPos);
  960.     End;
  961.   If WriteError = 0 Then
  962.     If Not AlreadyLocked Then
  963.       If Not UnlockMsgBase Then
  964.         WriteError := 5;
  965.   If ((WriteError = 0) and (HudsonFlushing)) Then
  966.     Begin
  967.     FlushFile(MsgRec^.MsgInfoFile);
  968.     FlushFile(MsgRec^.MsgTxtFile);
  969.     FlushFile(MsgRec^.MsgHdrFile);
  970.     FlushFile(MsgRec^.MsgInfoFile);
  971.     FlushFile(MsgRec^.MsgToIdxFile);
  972.     FlushFile(MsgRec^.MsgIdxFile);
  973.     End;
  974.   MsgRec^.MsgPos := MsgPos;;
  975.   WriteMsg := WriteError;
  976.   End;
  977.  
  978.  
  979. Procedure HudsonMsgObj.SetDest(Var Addr: AddrType);   {Set Zone/Net/Node/Point for Dest}
  980.   Begin
  981.   MsgRec^.MsgHdr.DestZone := Lo(Addr.Zone);
  982.   MsgRec^.MsgHdr.DestNet := Addr.Net;
  983.   MsgRec^.MsgHdr.DestNode := Addr.Node;
  984.   MsgRec^.DestPoint := Addr.Point;
  985.   If MsgRec^.DestPoint <> 0 Then
  986.     DoStringLn(#1 + 'TOPT ' + Long2Str(MsgRec^.DestPoint));
  987.   End;
  988.  
  989.  
  990. Procedure HudsonMsgObj.SetOrig(Var Addr: AddrType);   {Set Zone/Net/Node/Point for Orig}
  991.   Begin
  992.   MsgRec^.MsgHdr.OrigZone := Lo(Addr.Zone);
  993.   MsgRec^.MsgHdr.OrigNet := Addr.Net;
  994.   MsgRec^.MsgHdr.OrigNode := Addr.Node;
  995.   MsgRec^.OrigPoint := Addr.Point;
  996.   If MsgRec^.OrigPoint <> 0 Then
  997.     DoStringLn(#1 + 'FMPT ' + Long2Str(MsgRec^.OrigPoint));
  998.   End;
  999.  
  1000.  
  1001.  
  1002. Procedure HudsonMsgObj.SetMsgPath(MP: String);
  1003.   Begin
  1004.   MsgRec^.Area := Str2Long(Copy(MP,1,3));
  1005.   MsgRec^.MsgPath := Copy(MP,4,60);
  1006.   AddBackSlash(MsgRec^.MsgPath);
  1007.   shAssign(MsgRec^.MsgIdxFile, MsgRec^.MsgPath + 'MSGIDX.BBS');
  1008.   shAssign(MsgRec^.MsgToIdxFile, MsgRec^.MsgPath + 'MSGTOIDX.BBS');
  1009.   shAssign(MsgRec^.MsgHdrFile, MsgRec^.MsgPath + 'MSGHDR.BBS');
  1010.   shAssign(MsgRec^.MsgTxtFile, MsgRec^.MsgPath + 'MSGTXT.BBS');
  1011.   shAssign(MsgRec^.MsgInfoFile, MsgRec^.MsgPath + 'MSGINFO.BBS');
  1012.   End;
  1013.  
  1014.  
  1015. Function HudsonMsgObj.LockMsgBase: Boolean; {Lock msg base prior to adding message}
  1016.   Var
  1017.     LockError: Word;
  1018.     NumRead: Word;
  1019.  
  1020.   Begin
  1021.   LockError := 0;
  1022.   If Not MsgRec^.Locked Then
  1023.     Begin
  1024.     LockError := shLock(MsgRec^.MsgInfoFile,406,1);
  1025.     If LockError = 1 Then
  1026.       LockError := 0;                    {No Locking if share not loaded}
  1027.     If LockError = 0 Then
  1028.       Begin
  1029.       Seek(MsgRec^.MsgInfoFile,0);
  1030.       LockError := IoResult;
  1031.       End;
  1032.     If LockError = 0 Then
  1033.       Begin
  1034.       If Not shRead(MsgRec^.MsgInfoFile, MsgRec^.MsgInfo,1,NumRead) Then
  1035.         LockError := FileError;
  1036.       End;
  1037.     End;
  1038.   MsgRec^.Locked := (LockError = 0);
  1039.   LockMsgBase := LockError = 0;
  1040.   End;
  1041.  
  1042.  
  1043. Function HudsonMsgObj.UnlockMsgBase: Boolean; {Unlock msg base after adding message}
  1044.   Var
  1045.     LockError: Word;
  1046.  
  1047.   Begin
  1048.   LockError := 0;
  1049.   If MsgRec^.Locked Then
  1050.     Begin
  1051.     Seek(MsgRec^.MsgInfoFile,0);
  1052.     LockError := IoResult;
  1053.     shWrite(MsgRec^.MsgInfoFile, MsgRec^.MsgInfo,1);
  1054.     If LockError = 0 Then
  1055.       LockError := IoResult;
  1056.     LockError := UnLockFile(MsgRec^.MsgInfoFile,406,1);
  1057.     If LockError = 1 Then
  1058.       LockError := 0;                    {No locking if share not loaded}
  1059.     End;
  1060.   MsgRec^.Locked := False;
  1061.   UnlockMsgBase := LockError = 0;
  1062.   End;
  1063.  
  1064.  
  1065. Function HudsonMsgObj.GetNumActive: Word;
  1066.   Begin
  1067.   GetNumActive := MsgRec^.MsgInfo.Active;
  1068.   End;
  1069.  
  1070.  
  1071. Function HudsonMsgObj.GetHighMsgNum: LongInt;
  1072.   Begin
  1073.   GetHighMsgNum := MsgRec^.MsgInfo.HighMsg;
  1074.   End;
  1075.  
  1076.  
  1077. Function HudsonMsgObj.GetLowMsgNum: LongInt;
  1078.   Begin
  1079.   GetLowMsgNum := MsgRec^.MsgInfo.LowMsg;
  1080.   End;
  1081.  
  1082.  
  1083. Function HudsonMsgObj.CreateMsgBase(MaxMsg: Word; MaxDays: Word): Word;
  1084.   Var
  1085.     CreateError: Word;
  1086.     i: Word;
  1087.  
  1088.   Begin
  1089.   CreateError := 0;
  1090.   ReWrite(MsgRec^.MsgIdxFile, SizeOf(MsgIdxType));
  1091.   If CreateError = 0 Then
  1092.     CreateError := IoResult;
  1093.   ReWrite(MsgRec^.MsgToIdxFile, SizeOf(MsgToIdxType));
  1094.   If CreateError = 0 Then
  1095.     CreateError := IoResult;
  1096.   ReWrite(MsgRec^.MsgHdrFile, SizeOf(MsgHdrType));
  1097.   If CreateError = 0 Then
  1098.     CreateError := IoResult;
  1099.   ReWrite(MsgRec^.MsgTxtFile, SizeOf(MsgTxtType));
  1100.   If CreateError = 0 Then
  1101.     CreateError := IoResult;
  1102.   ReWrite(MsgRec^.MsgInfoFile, SizeOf(MsgInfoType));
  1103.   If CreateError = 0 Then
  1104.     CreateError := IoResult;
  1105.   MsgRec^.MsgInfo.LowMsg := 1;
  1106.   MsgRec^.MsgInfo.HighMsg := 0;
  1107.   MsgRec^.MsgInfo.Active := 0;
  1108.   For i := 1 to 200 Do
  1109.     MsgRec^.MsgInfo.AreaActive[i] := 0;
  1110.   If Not shWrite(MsgRec^.MsgInfoFile, MsgRec^.MsgInfo, 1) Then
  1111.     CreateError := FileError;
  1112.   Close(MsgRec^.MsgInfoFile);
  1113.   If CreateError = 0 Then
  1114.     CreateError := IoResult;
  1115.   Close(MsgRec^.MsgIdxFile);
  1116.   If CreateError = 0 Then
  1117.     CreateError := IoResult;
  1118.   Close(MsgRec^.MsgToIdxFile);
  1119.   If CreateError = 0 Then
  1120.     CreateError := IoResult;
  1121.   Close(MsgRec^.MsgTxtFile);
  1122.   If CreateError = 0 Then
  1123.     CreateError := IoResult;
  1124.   Close(MsgRec^.MsgHdrFile);
  1125.   If CreateError = 0 Then
  1126.     CreateError := IoResult;
  1127.   CreateMsgBase := CreateError;
  1128.   End;
  1129.  
  1130.  
  1131. Function HudsonMsgObj.Check: Word;          {Check if msg base is Ok}
  1132.   { 0 = ok, 1 = not there (create), 2 = corrupted}
  1133.   Var
  1134.     BaseSize: LongInt;
  1135.     Status: Word;
  1136.  
  1137.   Begin
  1138.   Status := 0;
  1139.   If (Not FileExist(MsgRec^.MsgPath + 'MSGINFO.BBS'))  Then
  1140.     Status := 1;
  1141.   If (Not FileExist(MsgRec^.MsgPath + 'MSGHDR.BBS'))  Then
  1142.     Begin
  1143.     If Status = 0 Then
  1144.       Status := 2;
  1145.     End
  1146.   Else
  1147.     Begin
  1148.     If Status = 1 Then
  1149.       Status := 2;
  1150.     End;
  1151.   If (Not FileExist(MsgRec^.MsgPath + 'MSGTXT.BBS'))  Then
  1152.     Begin
  1153.     If Status = 0 Then
  1154.       Status := 2;
  1155.     End
  1156.   Else
  1157.     Begin
  1158.     If Status = 1 Then
  1159.       Status := 2;
  1160.     End;
  1161.   If (Not FileExist(MsgRec^.MsgPath + 'MSGIDX.BBS')) Then
  1162.     Begin
  1163.     If Status = 0 Then
  1164.       Status := 2;
  1165.     End
  1166.   Else
  1167.     Begin
  1168.     If Status = 1 Then
  1169.       Status := 2;
  1170.     End;
  1171.   If (Not FileExist(MsgRec^.MsgPath + 'MSGTOIDX.BBS'))  Then
  1172.     Begin
  1173.     If Status = 0 Then
  1174.       Status := 2;
  1175.     End
  1176.   Else
  1177.     Begin
  1178.     If Status = 1 Then
  1179.       Status := 2;
  1180.     End;
  1181.   If Status = 0 Then
  1182.     Begin
  1183.     If SizeFile(MsgRec^.MsgPath + 'MSGINFO.BBS') <> SizeOf(MsgInfoType) Then
  1184.       Status := 2;
  1185.     End;
  1186.   If Status = 0 Then
  1187.     Begin
  1188.     BaseSize := SizeFile(MsgRec^.MsgPath + 'MSGHDR.BBS') Div SizeOf(MsgHdrType);
  1189.     If BaseSize <> (SizeFile(MsgRec^.MsgPath + 'MSGIDX.BBS') Div SizeOf(MsgIdxType)) Then
  1190.       Status := 2;
  1191.     If BaseSize <> (SizeFile(MsgRec^.MsgPath + 'MSGTOIDX.BBS') Div SizeOf(MsgToIdxType)) Then
  1192.       Status := 2;
  1193.     End;
  1194.   Check := Status;
  1195.   End;
  1196.  
  1197.  
  1198.  
  1199. Function HudsonMsgObj.MsgBaseSize:Word;
  1200.   Begin
  1201.   If Length(MsgRec^.MsgPath) > 0 Then
  1202.     Begin
  1203.     MsgBaseSize := FileSize(MsgRec^.MsgIdxFile);
  1204.     End
  1205.   Else
  1206.     MsgBaseSize := 0;
  1207.   End;
  1208.  
  1209.  
  1210. Function HudsonMsgObj.SeekEnd: Word;        {Seek to end of Msg Base Files}
  1211.   Var
  1212.     SeekError: Word;
  1213.  
  1214.   Begin
  1215.   SeekError := 0;
  1216.   Seek(MsgRec^.MsgIdxFile, FileSize(MsgRec^.MsgIdxFile));
  1217.   If SeekError = 0 Then
  1218.     SeekError := IoResult;
  1219.   Seek(MsgRec^.MsgToIdxFile, FileSize(MsgRec^.MsgToIdxFile));
  1220.   If SeekError = 0 Then
  1221.     SeekError := IoResult;
  1222.   Seek(MsgRec^.MsgTxtFile, FileSize(MsgRec^.MsgTxtFile));
  1223.   If SeekError = 0 Then
  1224.     SeekError := IoResult;
  1225.   Seek(MsgRec^.MsgHdrFile, FileSize(MsgRec^.MsgHdrFile));
  1226.   If SeekError = 0 Then
  1227.     SeekError := IoResult;
  1228.   SeekEnd := SeekError;
  1229.   End;
  1230.  
  1231.  
  1232. Function HudsonMsgObj.SeekMsgBasePos(Position: Word): Word; {Seek to pos of Msg Base File}
  1233.   Var
  1234.     SeekError: Word;
  1235.   Begin
  1236.   Seek(MsgRec^.MsgIdxFile, Position);
  1237.   SeekError := IoResult;
  1238.   Seek(MsgRec^.MsgToIdxFile, Position);
  1239.   If SeekError = 0 Then
  1240.     SeekError := IoResult;
  1241.   Seek(MsgRec^.MsgHdrFile, Position);
  1242.   If SeekError = 0 Then
  1243.     SeekError := IoResult;
  1244.   SeekMsgBasePos := SeekError;
  1245.   End;
  1246.  
  1247.  
  1248. Function HudsonMsgObj.WriteMailIdx(FN: String; MsgPos: Word): Word; {Write Netmail or EchoMail.Bbs}
  1249.   Var
  1250.     IdxFile: File;
  1251.     WriteError: Word;
  1252.     IdxName: String;
  1253.  
  1254.   Begin
  1255.   WriteError := 0;
  1256.   If Length(HudsonEcho) > 0 Then
  1257.     IdxName := WithBackSlash(HudsonEcho) + FN
  1258.   Else
  1259.     IdxName := MsgRec^.MsgPath + FN;
  1260.   shAssign(IdxFile, IdxName);
  1261.   FileMode := fmReadWrite + fmDenyNone;
  1262.   If FileExist(IdxName) Then
  1263.     Begin
  1264.     If Not shReset(IdxFile, SizeOf(MsgPos)) Then
  1265.       WriteError := FileError;
  1266.     End
  1267.   Else
  1268.     Begin
  1269.     ReWrite(IdxFile, SizeOf(MsgPos));
  1270.     WriteError := IoResult;
  1271.     End;
  1272.   If WriteError = 0 Then
  1273.     Begin
  1274.     Seek(IdxFile, FileSize(IdxFile));
  1275.     WriteError := IoResult;
  1276.     End;
  1277.   If WriteError = 0 Then
  1278.     Begin
  1279.     BlockWrite(IdxFile, MsgPos, 1);
  1280.     WriteError := IoResult;
  1281.     End;
  1282.   If WriteError = 0 Then
  1283.     Begin
  1284.     Close(IdxFile);
  1285.     WriteError := IoResult;
  1286.     End;
  1287.   WriteMailIdx := WriteError;
  1288.   End;
  1289.  
  1290.  
  1291. Function HudsonMsgObj.OpenMsgBase: Word; {Set path and initialize}
  1292.   Var
  1293.     OpenError: Word;
  1294.     CheckMode: Word;
  1295.     NumRead: Word;
  1296.  
  1297.   Begin
  1298.   OpenError := 0;
  1299.   If Not MsgRec^.Opened Then
  1300.     Begin
  1301.     CheckMode := Check;
  1302.     If CheckMode = 1 Then
  1303.       Begin
  1304.       OpenError := CreateMsgBase(100,100);
  1305.       If OpenError = 0 Then
  1306.         CheckMode := 0;
  1307.       End;
  1308.     If CheckMode = 2 Then
  1309.       OpenError := 5000;
  1310.     If CheckMode = 0 Then
  1311.       Begin
  1312.       FileMode := fmReadWrite + fmDenyNone;
  1313.       If Not ShReset(MsgRec^.MsgIdxFile, SizeOf(MsgIdxType)) Then
  1314.         OpenError := FileError;
  1315.       FileMode := fmReadWrite + fmDenyNone;
  1316.       If Not shReset(MsgRec^.MsgToIdxFile, SizeOf(MsgToIdxType)) Then
  1317.         OpenError := FileError;
  1318.       FileMode := fmReadWrite + fmDenyNone;
  1319.       If Not shReset(MsgRec^.MsgTxtFile, SizeOf(MsgTxtType)) Then
  1320.         OpenError := FileError;
  1321.       FileMode := fmReadWrite + fmDenyNone;
  1322.       If Not shReset(MsgRec^.MsgHdrFile, SizeOf(MsgHdrType)) Then
  1323.         OpenError := FileError;
  1324.       FileMode := fmReadWrite + fmDenyNone;
  1325.       If Not shReset(MsgRec^.MsgInfoFile, SizeOf(MsgInfoType)) Then
  1326.         OpenError := FileError;
  1327.       End;
  1328.     End;
  1329.   If OpenError = 0 Then
  1330.     Begin
  1331.     If Not shRead(MsgRec^.MsgInfoFile, MsgRec^.MsgInfo,1,NumRead) Then
  1332.       OpenError := 1;
  1333.     End;
  1334.   MsgRec^.Opened := (OpenError = 0);
  1335.   OpenMsgBase := OpenError;
  1336.   End;
  1337.  
  1338.  
  1339. Function HudsonMsgObj.CloseMsgBase: Word;         {Close Msg Base Files}
  1340.   Var
  1341.     CloseError: Word;
  1342.  
  1343.   Begin
  1344.   CloseError := 0;
  1345.   If MsgRec^.Opened Then
  1346.     Begin
  1347.     Close(MsgRec^.MsgIdxFile);
  1348.     If CloseError = 0 Then
  1349.       CloseError := IoResult;
  1350.     Close(MsgRec^.MsgToIdxFile);
  1351.     If CloseError = 0 Then
  1352.       CloseError := IoResult;
  1353.     Close(MsgRec^.MsgTxtFile);
  1354.     If CloseError = 0 Then
  1355.       CloseError := IoResult;
  1356.     Close(MsgRec^.MsgHdrFile);
  1357.     If CloseError = 0 Then
  1358.       CloseError := IoResult;
  1359.     Close(MsgRec^.MsgInfoFile);
  1360.     If CloseError = 0 Then
  1361.       CloseError := IoResult;
  1362.     End;
  1363.   CloseMsgBase := CloseError;
  1364.   End;
  1365.  
  1366.  
  1367. Procedure HudsonMsgObj.SeekRead(NumToRead: Word);
  1368.   Begin
  1369.   If NumToRead > SeekSize Then
  1370.     NumToRead := SeekSize;
  1371.   Seek(MsgRec^.MsgIdxFile, MsgRec^.SeekStart);
  1372.   If IoResult <> 0 Then;
  1373.   If Not shRead(MsgRec^.MsgIdxFile, SeekArray^, NumToRead , MsgRec^.SeekNumRead) Then
  1374.     MsgRec^.Error := 1000;
  1375.   End;
  1376.  
  1377.  
  1378. Procedure HudsonMsgObj.SeekNext;
  1379.   Var
  1380.     SDone: Boolean;
  1381.  
  1382.   Begin
  1383.   SDone := False;
  1384.   While Not SDone Do
  1385.     Begin
  1386.     Inc(MsgRec^.SeekPos);
  1387.     If (MsgRec^.SeekPos > MsgRec^.SeekNumRead) Then
  1388.       Begin
  1389.       Inc(MsgRec^.SeekStart, MsgRec^.SeekNumRead);
  1390.       SeekRead(SeekSize);
  1391.       MsgRec^.SeekPos := 1;
  1392.       End;
  1393.     If MsgRec^.SeekNumRead = 0 Then
  1394.       Begin
  1395.       MsgRec^.SeekOver := True;
  1396.       SDone := True;
  1397.       End
  1398.     Else
  1399.       Begin
  1400.       If ((SeekArray^[MsgRec^.SeekPos].MsgNum > MsgRec^.CurrMsgNum) And
  1401.       (SeekArray^[MsgRec^.SeekPos].MsgNum <> $ffff) And
  1402.       (SeekArray^[MsgRec^.SeekPos].Area = MsgRec^.Area) And
  1403.       (MsgRec^.SeekPos > 0) And (MsgRec^.SeekPos <= MsgRec^.SeekNumRead)) Then
  1404.         Begin
  1405.         SDone := True;
  1406.         MsgRec^.CurrMsgNum := SeekArray^[MsgRec^.SeekPos].MsgNum;
  1407.         End;
  1408.       End;
  1409.     End;
  1410.   MsgRec^.MsgPos := MsgRec^.SeekStart + MsgRec^.SeekPos - 1;
  1411.   End;
  1412.  
  1413.  
  1414. Procedure HudsonMsgObj.SeekPrior;
  1415.   Var
  1416.     SDone: Boolean;
  1417.     SeekDec: Word;
  1418.  
  1419.   Begin
  1420.   MsgRec^.SeekOver := False;
  1421.   SDone := False;
  1422.   While Not SDone Do
  1423.     Begin
  1424.     Dec(MsgRec^.SeekPos);
  1425.     If (MsgRec^.SeekPos < 1) Then
  1426.       Begin
  1427.       If MsgRec^.SeekStart = 0 Then
  1428.         Begin
  1429.         MsgRec^.SeekOver := True;
  1430.         SDone := True;
  1431.         End;
  1432.       If (MsgRec^.SeekStart < SeekSize) Then
  1433.         SeekDec := MsgRec^.SeekStart
  1434.       Else
  1435.         SeekDec := SeekSize;
  1436.       Dec(MsgRec^.SeekStart, SeekDec);
  1437.       If MsgRec^.SeekStart < 0 Then
  1438.         MsgRec^.SeekStart := 0;
  1439.       SeekRead(SeekDec);
  1440.       MsgRec^.SeekPos := MsgRec^.SeekNumRead;
  1441.       End;
  1442.     If Not MsgRec^.SeekOver Then
  1443.       Begin
  1444.       If ((SeekArray^[MsgRec^.SeekPos].MsgNum < MsgRec^.CurrMsgNum) And
  1445.       (SeekArray^[MsgRec^.SeekPos].MsgNum <> $ffff) And
  1446.       (SeekArray^[MsgRec^.SeekPos].Area = MsgRec^.Area) And
  1447.       (MsgRec^.SeekPos > 0) And (MsgRec^.SeekPos <= MsgRec^.SeekNumRead)) Then
  1448.         Begin
  1449.         SDone := True;
  1450.         MsgRec^.CurrMsgNum := SeekArray^[MsgRec^.SeekPos].MsgNum;
  1451.         End;
  1452.       End;
  1453.     End;
  1454.   MsgRec^.MsgPos := MsgRec^.SeekStart + MsgRec^.SeekPos - 1;
  1455.   End;
  1456.  
  1457.  
  1458. Function HudsonMsgObj.SeekFound:Boolean;   {Seek has been completed}
  1459.   Begin
  1460.   SeekFound := Not MsgRec^.SeekOver;
  1461.   End;
  1462.  
  1463.  
  1464. Procedure HudsonMsgObj.SeekFirst(MsgNum: LongInt);
  1465.   Begin
  1466.   MsgRec^.SeekStart := 0;
  1467.   MsgRec^.SeekNumRead := 0;
  1468.   MsgRec^.SeekPos := 0;
  1469.   MsgRec^.SeekOver := False;
  1470.   SeekRead(SeekSize);
  1471.   MsgRec^.CurrMsgNum := MsgNum - 1;
  1472.   SeekNext;
  1473.   End;
  1474.  
  1475.  
  1476. Procedure HudsonMsgObj.SetMailType(MT: MsgMailType);
  1477.   Begin
  1478.   MsgRec^.MT := MT;
  1479.   End;
  1480.  
  1481.  
  1482. Function HudsonMsgObj.GetSubArea: Word;
  1483.   Begin
  1484.   GetSubArea := MsgRec^.MsgHdr.Area;
  1485.   End;
  1486.  
  1487.  
  1488. Procedure HudsonMsgObj.ReWriteHdr;
  1489.   Var
  1490.     NumRead: Word;
  1491.     RcvdName: String[35];
  1492.     MsgError: Word;
  1493.     MsgIdx: MsgIdxType;
  1494.  
  1495.   Begin
  1496.   MsgError := SeekMsgBasePos(MsgRec^.MsgPos);
  1497.   If IsRcvd Then
  1498.     RcvdName := '* Received *'
  1499.   Else
  1500.     RcvdName := MsgRec^.MsgHdr.MsgTo;
  1501.   If IsDeleted Then
  1502.     Begin
  1503.     RcvdName := '* Deleted *';
  1504.     MsgIdx.MsgNum := $ffff;
  1505.     End
  1506.   Else
  1507.     MsgIdx.MsgNum := MsgRec^.MsgHdr.MsgNum;
  1508.   If MsgError = 0 Then
  1509.     Begin
  1510.     If not shWrite(MsgRec^.MsgHdrFile, MsgRec^.MsgHdr,1) Then
  1511.       MsgError := FileError;
  1512.     End;
  1513.   If MsgError = 0 Then
  1514.     Begin
  1515.     If Not shWrite(MsgRec^.MsgToIdxFile, RcvdName, 1) Then
  1516.       MsgError := FileError;
  1517.     End;
  1518.   MsgIdx.Area := MsgRec^.MsgHdr.Area;
  1519.   If MsgError = 0 Then
  1520.     Begin
  1521.     If not shWrite(MsgRec^.MsgIdxFile, MsgIdx,1) Then
  1522.       MsgError := FileError;
  1523.     End;
  1524.   End;
  1525.  
  1526.  
  1527. Procedure HudsonMsgObj.DeleteMsg;
  1528.   Var
  1529.     NumRead: Word;
  1530.     RcvdName: String[35];
  1531.     MsgIdx: MsgIdxType;
  1532.     MsgError: Word;
  1533.  
  1534.   Begin
  1535.   MsgIdx.Area := MsgRec^.MsgHdr.Area;
  1536.   If LockMsgBase Then
  1537.     MsgError := 0
  1538.   Else
  1539.     MsgError := 5;
  1540.   If MsgError = 0 Then
  1541.     MsgError := SeekMsgBasePos(MsgRec^.MsgPos);
  1542.   If MsgError = 0 Then
  1543.     Begin
  1544.     If not shRead(MsgRec^.MsgHdrFile, MsgRec^.MsgHdr,1, NumRead) Then
  1545.       MsgError := FileError;
  1546.     End;
  1547.   If ((MsgRec^.MsgHdr.MsgAttr or maDeleted) = 0) Then
  1548.     Begin
  1549.     Dec(MsgRec^.MsgInfo.Active);
  1550.     Dec(MsgRec^.MsgInfo.AreaActive[MsgRec^.MsgHdr.Area]);
  1551.     End;
  1552.   MsgRec^.MsgHdr.MsgAttr := MsgRec^.MsgHdr.MsgAttr Or maDeleted;
  1553.   RcvdName := '* Deleted *';
  1554.   MsgIdx.MsgNum := $ffff;
  1555.   If MsgError = 0 Then
  1556.     MsgError := SeekMsgBasePos(MsgRec^.MsgPos);
  1557.   If MsgError = 0 Then
  1558.     Begin
  1559.     If not shWrite(MsgRec^.MsgHdrFile, MsgRec^.MsgHdr,1) Then
  1560.       MsgError := FileError;
  1561.     End;
  1562.   If MsgError = 0 Then
  1563.     If Not shWrite(MsgRec^.MsgToIdxFile, RcvdName, 1) Then
  1564.       MsgError := FileError;
  1565.   If MsgError = 0 Then
  1566.     If Not shWrite(MsgRec^.MsgIdxFile, MsgIdx, 1) Then
  1567.       MsgError := FileError;
  1568.   If MsgError = 0 Then
  1569.     If Not UnLockMsgBase Then
  1570.       MsgError := 5;
  1571.   End;
  1572.  
  1573.  
  1574. Function HudsonMsgObj.GetMsgLoc: LongInt;
  1575.   Begin
  1576.   GetMsgLoc := MsgRec^.MsgPos;
  1577.   End;
  1578.  
  1579.  
  1580. Procedure HudsonMsgObj.SetMsgLoc(ML: LongInt);
  1581.   Begin
  1582.   MsgRec^.MsgPos := ML;
  1583.   End;
  1584.  
  1585.  
  1586. Function HudsonMsgObj.NumberOfMsgs: LongInt;
  1587.   Var
  1588.     TmpInfo: MsgInfoType;
  1589.  
  1590.   Begin
  1591.   If LoadFile(MsgRec^.MsgPath + 'MsgInfo.Bbs', TmpInfo, SizeOf(TmpInfo)) = 0 Then
  1592.     NumberOfMsgs := TmpInfo.Active
  1593.   Else
  1594.     NumberOfMsgs := 0;
  1595.   End;
  1596.  
  1597.  
  1598. Procedure HudsonMsgObj.GetAllLastRead(UNum: LongInt; Var LR: LastReadType);
  1599.   Var
  1600.     LastName: String;
  1601.  
  1602.   Begin
  1603.   If Length(HudsonLast) > 0 Then
  1604.     LastName := WithBackSlash(HudsonLast) + 'LastRead.Bbs'
  1605.   Else
  1606.     LastName := MsgRec^.MsgPath + 'LastRead.Bbs';
  1607.   FillChar(LR, SizeOf(LR), 0);
  1608.   If ((UNum + 1) * SizeOf(LastReadType)) <=
  1609.   SizeFile(LastName) Then
  1610.     Begin
  1611.     If LoadFilePos(LastName, LR, SizeOf(LR),
  1612.     UNum * SizeOf(LastReadType)) = 0 Then;
  1613.     End;
  1614.   End;
  1615.  
  1616.  
  1617.  
  1618.  
  1619. Function HudsonMsgObj.GetLastRead(UNum: LongInt): LongInt;
  1620.   Var
  1621.     LRec: LastReadType;
  1622.     LastName: String;
  1623.  
  1624.   Begin
  1625.   If Length(HudsonLast) > 0 Then
  1626.     LastName := WithBackSlash(HudsonLast) + 'LastRead.Bbs'
  1627.   Else
  1628.     LastName := MsgRec^.MsgPath + 'LastRead.Bbs';
  1629.   If ((UNum + 1) * SizeOf(LastReadType)) >
  1630.   SizeFile(LastName) Then
  1631.     GetLastRead := 0
  1632.   Else
  1633.     Begin
  1634.     If LoadFilePos(LastName, LRec, SizeOf(LRec),
  1635.     UNum * SizeOf(LastReadType)) = 0 Then
  1636.       GetLastRead := LRec[MsgRec^.Area]
  1637.     Else
  1638.       GetLastRead := 0;
  1639.     End;
  1640.   End;
  1641.  
  1642.  
  1643. Procedure HudsonMsgObj.SetLastRead(UNum: LongInt; LR: LongInt);
  1644.   Var
  1645.     LRec: LastReadType;
  1646.     Status: Word;
  1647.     LastName: String;
  1648.  
  1649.   Begin
  1650.   If Length(HudsonLast) > 0 Then
  1651.     LastName := WithBackSlash(HudsonLast) + 'LastRead.Bbs'
  1652.   Else
  1653.     LastName := MsgRec^.MsgPath + 'LastRead.Bbs';
  1654.   If ((UNum + 1) * SizeOf(LastReadType)) >  SizeFile(LastName) Then
  1655.     Begin
  1656.     Status := ExtendFile(LastName,(UNum + 1) * SizeOf(LastReadType));
  1657.     End;
  1658.   If LoadFilePos(LastName, LRec, SizeOf(LRec), UNum * SizeOf(LastReadType)) = 0 Then
  1659.     Begin
  1660.     LRec[MsgRec^.Area] := LR;
  1661.     Status := SaveFilePos(LastName, LRec, SizeOf(LRec),
  1662.     UNum * SizeOf(LastReadType));
  1663.     End;
  1664.   End;
  1665.  
  1666.  
  1667. Procedure HudsonMsgObj.GetHighest(Var LR: LastReadType);
  1668.   Var
  1669.     i: Word;
  1670.     IdxFile: File;
  1671.     MIdx: ^SeekArrayType;
  1672.     NumRead: Word;
  1673.  
  1674.   Begin
  1675.   New(MIdx);
  1676.   For i := 1 to 200 Do
  1677.     LR[i] := 0;
  1678.   Assign(IdxFile, MsgRec^.MsgPath + 'MsgIdx.Bbs');
  1679.   FileMode := fmReadOnly + fmDenyNone;
  1680.   If shReset(IdxFile, SizeOf(MsgIdxType)) Then;
  1681.   While Not(Eof(IdxFile)) Do
  1682.     Begin
  1683.     If shRead(IdxFile, MIdx^, SeekSize, NumRead) Then;
  1684.     i := 1;
  1685.     While i <= NumRead Do
  1686.       Begin
  1687.       If MIdx^[i].MsgNum <> $ffff Then
  1688.         Begin
  1689.         If MIdx^[i].MsgNum > LR[MIdx^[i].Area] Then
  1690.           LR[MIdx^[i].Area] := MIdx^[i].MsgNum;
  1691.         End;
  1692.       Inc(i);
  1693.       End;
  1694.     End;
  1695.   Close(IdxFile);
  1696.   If IoResult <> 0 Then;
  1697.   Dispose(MIdx);
  1698.   End;
  1699.  
  1700.  
  1701. Function HudsonMsgObj.GetTxtPos: LongInt;
  1702.   Var
  1703.     Tmp: LongInt;
  1704.  
  1705.   Begin
  1706.   Tmp := MsgRec^.CurrTxtRec;
  1707.   GetTxtPos := MsgRec^.CurrTxtPos +  Tmp shl 16;
  1708.   End;
  1709.  
  1710.  
  1711. Procedure HudsonMsgObj.SetTxtPos(TP: LongInt);
  1712.   Begin
  1713.   MsgRec^.CurrTxtRec := TP shr 16;
  1714.   MsgRec^.CurrTxtPos := TP and $ff;
  1715.   End;
  1716.  
  1717.  
  1718. End.
  1719.